#pragma once

#include <vector>
#include <pthread.h>
#include <semaphore.h>

const size_t N = 5;

template<class T>
class RingQueue
{
public:
    RingQueue(int num = N)
        :_v(num)
        ,_cap(num)
        ,p_pos(0)
        ,c_pos(0)
    {
        sem_init(&_ps,0,num); //生产者信号量 - 一开始生产者有num个位置可以生产
        sem_init(&_cs,0,0); //消费者信号量 - 一开始消费者没有可消费的商品

        pthread_mutex_init(&_pm,nullptr);
        pthread_mutex_init(&_cm,nullptr);
    }

    void push(const T& data) //生产者生产
    {
        //生产者生产商品...

        P(_ps); //申请一个生产信号量
        lock(_pm); //生产者锁 上锁 防止多个线程向一个位置写入资源
        _v[p_pos++] = data; //放入资源
        p_pos %= _cap; //下标矫正 防止越界
        unlock(_pm);
        V(_cs); //通知消费者 有一个商品被生产进入队列 可消费
    }

    void pop(T* data) //消费者消费
    {
        P(_cs); //申请一个消费信号量
        lock(_cm); //消费者锁 防止多个消费者在同一个位置读取数据
        *data = _v[c_pos++];
        c_pos %= _cap;
        unlock(_cm);
        V(_ps); //通知生产者有一个位置可以生产商品

        //消费者处理商品
    }
    

private: //内部函数
    void P(sem_t& p) { sem_wait(&p); } //生产者生产
    void V(sem_t& v) { sem_post(&v); } //消费者消费

    void lock(pthread_mutex_t& mtx) { pthread_mutex_lock(&mtx);} //上锁
    void unlock(pthread_mutex_t& mtx) { pthread_mutex_unlock(&mtx);} //解锁

private: //资源
    std::vector<T> _v; //循环队列
    size_t _cap; //循环队列大小

    size_t p_pos; //生产者下标
    size_t c_pos; //消费者下标

    sem_t _ps,_cs; //信号量(资源数)
    pthread_mutex_t _pm,_cm; //锁 防止并发问题
    
};